package com.code2roc.fastface.util;

import com.code2roc.fastface.eums.FaceEnum;
import com.seeta.proxy.FaceDetectorProxy;
import com.seeta.proxy.FaceLandmarkerProxy;
import com.seeta.proxy.FaceRecognizerProxy;
import com.seeta.sdk.*;
import com.seeta.sdk.util.LoadNativeCore;
import com.seeta.sdk.util.SeetafaceUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.awt.image.BufferedImage;
import java.io.File;

@Component
public class SeetaFaceHepler {
    @Autowired
    private FaceDetectorProxy faceDetectorProxy;
    @Autowired
    private FaceRecognizerProxy faceRecognizerProxy;
    @Autowired
    private FaceLandmarkerProxy faceLandmarkerProxy;
    @Value("${face.modelPath}")
    private String modelPath;
    @Value("${face.dataBasePath}")
    private String dataBasePath;
    private FaceDatabase faceDatabase;
    private float CHECK_SIM = new Float(0.62);

    @PostConstruct
    public void init() throws Exception {
        LoadNativeCore.LOAD_NATIVE(SeetaDevice.SEETA_DEVICE_CPU);
        faceDatabase = new FaceDatabase(new SeetaModelSetting(0, new String[]{modelPath + File.separator + "face_recognizer.csta"}, SeetaDevice.SEETA_DEVICE_CPU));
        File file = new File(dataBasePath);
        if (!file.exists()) {
            file.createNewFile();
        }
        faceDatabase.Load(dataBasePath);
    }

    public FaceEnum.CheckImageFaceStatus getFace(BufferedImage image) throws Exception {
        SeetaImageData imageData = SeetafaceUtil.toSeetaImageData(image);
        SeetaRect[] detects = faceDetectorProxy.detect(imageData);
        if (detects.length == 0) {
            return FaceEnum.CheckImageFaceStatus.NoFace;
        } else if (detects.length == 1) {
            return FaceEnum.CheckImageFaceStatus.OneFace;
        } else {
            return FaceEnum.CheckImageFaceStatus.MoreFace;
        }
    }

    public FaceEnum.CompareImageFaceStatus compareFace(BufferedImage source, BufferedImage compare) throws Exception {
        float[] sourceFeature = extract(source);
        float[] compareFeature = extract(compare);
        if (sourceFeature != null && compareFeature != null) {
            float calculateSimilarity = faceRecognizerProxy.calculateSimilarity(sourceFeature, compareFeature);
            System.out.printf("相似度:%f\n", calculateSimilarity);
            if (calculateSimilarity >= CHECK_SIM) {
                return FaceEnum.CompareImageFaceStatus.Same;
            } else {
                return FaceEnum.CompareImageFaceStatus.Different;
            }
        } else {
            return FaceEnum.CompareImageFaceStatus.LostFace;
        }
    }

    public long registFace(BufferedImage image) throws Exception {
        long result = -1;
        SeetaImageData imageData = SeetafaceUtil.toSeetaImageData(image);
        SeetaRect[] detects = faceDetectorProxy.detect(imageData);
        if (detects.length > 0) {
            SeetaPointF[] pointFS = faceLandmarkerProxy.mark(imageData, detects[0]);
            result = faceDatabase.Register(imageData, pointFS);
            faceDatabase.Save(dataBasePath);
        }
        return result;


    }

    public long queryFace(BufferedImage image) throws Exception {
        long result = -1;
        SeetaImageData imageData = SeetafaceUtil.toSeetaImageData(image);
        SeetaRect[] detects = faceDetectorProxy.detect(imageData);
        if (detects.length > 0) {
            SeetaPointF[] pointFS = faceLandmarkerProxy.mark(imageData, detects[0]);
            long[] index = new long[1];
            float[] sim = new float[1];
            result = faceDatabase.QueryTop(imageData, pointFS, 1, index, sim);
            if (result > 0) {
                float similarity = sim[0];
                if (similarity >= CHECK_SIM) {
                    result = index[0];
                } else {
                    result = -1;
                }
            }
        }
        return result;
    }

    public long deleteFace(long index) throws Exception {
        long result = faceDatabase.Delete(index);
        faceDatabase.Save(dataBasePath);
        return result;
    }

    private float[] extract(BufferedImage image) throws Exception {
        SeetaImageData imageData = SeetafaceUtil.toSeetaImageData(image);
        SeetaRect[] detects = faceDetectorProxy.detect(imageData);
        if (detects.length > 0) {
            SeetaPointF[] pointFS = faceLandmarkerProxy.mark(imageData, detects[0]);
            float[] features = faceRecognizerProxy.extract(imageData, pointFS);
            return features;
        }
        return null;
    }

    public void resetFaceDataBase() {
        faceDatabase.Clear();
    }
}
